`timescale 1ns / 1ps

`include "data_width.vh"

module double_fpga_mem #(
    MEM_DWIDTH              = `MEM_DWIDTH,
    MEM_AWIDTH              = `MEM_AWIDTH,
    OFFSET_CHANNEL_WIDTH    = `OFFSET_CHANNEL_WIDTH,
    INFO_CHANNEL_WIDTH      = `INFO_CHANNEL_WIDTH,
    DEGREE_CHANNEL_WIDTH    = `DEGREE_CHANNEL_WIDTH
    ) (
        input                                   clk,
        input                                   front_rst,

        // axi interface
        input                                   s_Offset_axis_tvalid_1,
        input                                   s_Offset_axis_tvalid_2,
        input [OFFSET_CHANNEL_WIDTH - 1 : 0]    s_Offset_axis_tdata_1,
        input [OFFSET_CHANNEL_WIDTH - 1 : 0]    s_Offset_axis_tdata_2,
        input                                   s_Offset_axis_tlast_1,
        input                                   s_Offset_axis_tlast_2,
        output                                  s_Offset_axis_tready_1,
        output                                  s_Offset_axis_tready_2,

        input                                   s_Info_axis_tvalid_1,
        input                                   s_Info_axis_tvalid_2,
        input [INFO_CHANNEL_WIDTH - 1 : 0]      s_Info_axis_tdata_1,
        input [INFO_CHANNEL_WIDTH - 1 : 0]      s_Info_axis_tdata_2,
        input                                   s_Info_axis_tlast_1,
        input                                   s_Info_axis_tlast_2,
        output                                  s_Info_axis_tready_1,
        output                                  s_Info_axis_tready_2,

        input                                   s_Degree_axis_tvalid_1,
        input                                   s_Degree_axis_tvalid_2,
        input [DEGREE_CHANNEL_WIDTH - 1 : 0]    s_Degree_axis_tdata_1,
        input [DEGREE_CHANNEL_WIDTH - 1 : 0]    s_Degree_axis_tdata_2,
        input                                   s_Degree_axis_tlast_1,
        input                                   s_Degree_axis_tlast_2,
        output                                  s_Degree_axis_tready_1,
        output                                  s_Degree_axis_tready_2,

        // Accelerator interface
        input [MEM_AWIDTH - 1 : 0]              addr_offset,
        input [MEM_AWIDTH - 1 : 0]              addr_info,
        input                                   addr_offset_valid,
        input                                   addr_info_valid,

        output reg                              rst,
        output                                  mem_full,
        output [MEM_DWIDTH * 2 - 1 : 0]         mem_data_offset,
        output [MEM_DWIDTH * 2 - 1 : 0]         mem_data_info,
        output [MEM_DWIDTH * 2 - 1 : 0]         mem_data_degree,
        output [MEM_AWIDTH - 1 : 0]             mem_addr_offset,
        output [MEM_AWIDTH - 1 : 0]             mem_addr_info,
        output                                  mem_data_valid_offset,
        output                                  mem_data_valid_info);
    
    wire                buffer_empty_offset, buffer_full_offset;
    wire                buffer_empty_info, buffer_full_info;
    wire [2 - 1 : 0]    fifo_empty_offset, fifo_full_offset;
    wire [2 - 1 : 0]    fifo_empty_info, fifo_full_info;
    wire [2 - 1 : 0]    fifo_empty_degree, fifo_full_degree;
    wire                read_next_offset, read_next_info, read_next_degree;

    assign s_Offset_axis_tready_1   = !fifo_full_offset[0] && !front_rst;
    assign s_Offset_axis_tready_2   = !fifo_full_offset[1] && !front_rst;
    assign s_Info_axis_tready_1     = !fifo_full_info[0] && !front_rst;
    assign s_Info_axis_tready_2     = !fifo_full_info[1] && !front_rst;
    assign s_Degree_axis_tready_1   = !fifo_full_degree[0] && !front_rst;
    assign s_Degree_axis_tready_2   = !fifo_full_degree[1] && !front_rst;

    assign read_next_offset     = !(|fifo_empty_offset | buffer_empty_offset);
    assign read_next_info       = !(|fifo_empty_info | buffer_empty_info | (|fifo_empty_degree));
    assign read_next_degree     = read_next_info;

    assign mem_full = buffer_full_offset || buffer_full_info;

    always @ (posedge clk) begin
        rst <= front_rst;
    end

    addr_32bit_fifo Mem_Addr_Offset_Fifo (
        .clk(clk), .srst(front_rst),
        .din(addr_offset), .wr_en(addr_offset_valid), .rd_en(read_next_offset),

        .dout(mem_addr_offset), .valid(), // not use
        .empty(buffer_empty_offset), .prog_full(buffer_full_offset));

    mem_data_fifo Mem_Data_Offset_Fifo_1 (
        .clk(clk), .srst(front_rst),
        .din(s_Offset_axis_tdata_1), .wr_en(s_Offset_axis_tvalid_1 & s_Offset_axis_tready_1), .rd_en(read_next_offset),

        .dout(mem_data_offset[MEM_DWIDTH - 1 : 0]), .valid(mem_data_valid_offset),
        .empty(fifo_empty_offset[0]), .prog_full(fifo_full_offset[0]));

    mem_data_fifo Mem_Data_Offset_Fifo_2 (
        .clk(clk), .srst(front_rst),
        .din(s_Offset_axis_tdata_2), .wr_en(s_Offset_axis_tvalid_2 & s_Offset_axis_tready_2), .rd_en(read_next_offset),

        .dout(mem_data_offset[MEM_DWIDTH * 2 - 1 : MEM_DWIDTH]), .valid(),
        .empty(fifo_empty_offset[1]), .prog_full(fifo_full_offset[1]));

    addr_32bit_fifo Mem_Addr_Info_Fifo (
        .clk(clk), .srst(front_rst),
        .din(addr_info), .wr_en(addr_info_valid), .rd_en(read_next_info),

        .dout(mem_addr_info), .valid(), // not use
        .empty(buffer_empty_info), .prog_full(buffer_full_info));

    mem_data_fifo Mem_Data_Info_Fifo_1 (
        .clk(clk), .srst(front_rst),
        .din(s_Info_axis_tdata_1), .wr_en(s_Info_axis_tvalid_1 & s_Info_axis_tready_1), .rd_en(read_next_info),

        .dout(mem_data_info[MEM_DWIDTH - 1 : 0]), .valid(mem_data_valid_info),
        .empty(fifo_empty_info[0]), .prog_full(fifo_full_info[0]));

     mem_data_fifo Mem_Data_Info_Fifo_2 (
        .clk(clk), .srst(front_rst),
        .din(s_Info_axis_tdata_2), .wr_en(s_Info_axis_tvalid_2 & s_Info_axis_tready_2), .rd_en(read_next_info),

        .dout(mem_data_info[MEM_DWIDTH * 2 - 1 : MEM_DWIDTH]), .valid(),
        .empty(fifo_empty_info[1]), .prog_full(fifo_full_info[1]));

    mem_data_fifo Mem_Data_Degree_Fifo_1 (
        .clk(clk), .srst(front_rst),
        .din(s_Degree_axis_tdata_1),
        .wr_en(s_Degree_axis_tvalid_1 & s_Degree_axis_tready_1),
        .rd_en(read_next_degree),

        .dout(mem_data_degree[MEM_DWIDTH - 1 : 0]),
        .empty(fifo_empty_degree[0]), .prog_full(fifo_full_degree[0]));

     mem_data_fifo Mem_Data_Degree_Fifo_2 (
        .clk(clk), .srst(front_rst),
        .din(s_Degree_axis_tdata_2),
        .wr_en(s_Degree_axis_tvalid_2 & s_Degree_axis_tready_2),
        .rd_en(read_next_degree),

        .dout(mem_data_degree[MEM_DWIDTH * 2 - 1 : MEM_DWIDTH]),
        .empty(fifo_empty_degree[1]), .prog_full(fifo_full_degree[1]));

endmodule